1   /*
2    * Copyright (C) 2012 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.google.common.collect.testing.google;
17  
18  import static com.google.common.base.Preconditions.checkState;
19  import static com.google.common.collect.testing.features.CollectionSize.ZERO;
20  import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS;
21  import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES;
22  import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT;
23  import static com.google.common.truth.Truth.assertThat;
24  
25  import com.google.common.annotations.GwtCompatible;
26  import com.google.common.collect.Iterators;
27  import com.google.common.collect.Lists;
28  import com.google.common.collect.Multimap;
29  import com.google.common.collect.testing.features.CollectionSize;
30  import com.google.common.collect.testing.features.MapFeature;
31  
32  import java.util.Collection;
33  import java.util.Collections;
34  import java.util.Iterator;
35  
36  /**
37   * Tests for {@link Multimap#putAll(Object, Iterable)}.
38   *
39   * @author Louis Wasserman
40   */
41  @GwtCompatible
42  public class MultimapPutIterableTester<K, V> extends AbstractMultimapTester<K, V, Multimap<K, V>> {
43    @CollectionSize.Require(absent = ZERO)
44    @MapFeature.Require(SUPPORTS_PUT)
45    public void testPutAllNonEmptyIterableOnPresentKey() {
46      assertTrue(multimap().putAll(sampleKeys().e0, new Iterable<V>() {
47        @Override
48        public Iterator<V> iterator() {
49          return Lists.newArrayList(sampleValues().e3, sampleValues().e4).iterator();
50        }
51      }));
52      assertGet(sampleKeys().e0, sampleValues().e0, sampleValues().e3, sampleValues().e4);
53    }
54    
55    @CollectionSize.Require(absent = ZERO)
56    @MapFeature.Require(SUPPORTS_PUT)
57    public void testPutAllNonEmptyCollectionOnPresentKey() {
58      assertTrue(multimap().putAll(
59          sampleKeys().e0, Lists.newArrayList(sampleValues().e3, sampleValues().e4)));
60      assertGet(sampleKeys().e0, sampleValues().e0, sampleValues().e3, sampleValues().e4);
61    }
62  
63    @MapFeature.Require(SUPPORTS_PUT)
64    public void testPutAllNonEmptyIterableOnAbsentKey() {
65      assertTrue(multimap().putAll(sampleKeys().e3, new Iterable<V>() {
66        @Override
67        public Iterator<V> iterator() {
68          return Lists.newArrayList(sampleValues().e3, sampleValues().e4).iterator();
69        }
70      }));
71      assertGet(sampleKeys().e3, sampleValues().e3, sampleValues().e4);
72    }
73  
74    @MapFeature.Require(SUPPORTS_PUT)
75    public void testPutAllNonEmptyCollectionOnAbsentKey() {
76      assertTrue(multimap().putAll(
77          sampleKeys().e3, Lists.newArrayList(sampleValues().e3, sampleValues().e4)));
78      assertGet(sampleKeys().e3, sampleValues().e3, sampleValues().e4);
79    }
80    
81    @CollectionSize.Require(absent = ZERO)
82    @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
83    public void testPutAllNullValueOnPresentKey_supported() {
84      assertTrue(multimap().putAll(sampleKeys().e0, Lists.newArrayList(sampleValues().e3, null)));
85      assertGet(sampleKeys().e0, sampleValues().e0, sampleValues().e3, null);
86    }
87    
88    @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
89    public void testPutAllNullValueOnAbsentKey_supported() {
90      assertTrue(multimap().putAll(sampleKeys().e3, Lists.newArrayList(sampleValues().e3, null)));
91      assertGet(sampleKeys().e3, sampleValues().e3, null);
92    }
93  
94    @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES)
95    public void testPutAllNullValueSingle_unsupported() {
96      multimap().putAll(sampleKeys().e1, Lists.newArrayList((V) null));
97      expectUnchanged();
98    }
99  
100   // In principle, it would be nice to apply these two tests to keys with existing values, too.
101   
102   @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES)
103   public void testPutAllNullValueNullLast_unsupported() {
104     int size = getNumElements();
105 
106     try {
107       multimap().putAll(sampleKeys().e3, Lists.newArrayList(sampleValues().e3, null));
108       fail();
109     } catch (NullPointerException expected) {
110     }
111 
112     Collection<V> values = multimap().get(sampleKeys().e3);
113     if (values.size() == 0) {
114       expectUnchanged();
115       // Be extra thorough in case internal state was corrupted by the expected null.
116       assertEquals(Lists.newArrayList(), Lists.newArrayList(values));
117       assertEquals(size, multimap().size());
118     } else {
119       assertEquals(Lists.newArrayList(sampleValues().e3), Lists.newArrayList(values));
120       assertEquals(size + 1, multimap().size());
121     }
122   }
123 
124   @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES)
125   public void testPutAllNullValueNullFirst_unsupported() {
126     int size = getNumElements();
127 
128     try {
129       multimap().putAll(sampleKeys().e3, Lists.newArrayList(null, sampleValues().e3));
130       fail();
131     } catch (NullPointerException expected) {
132     }
133 
134     /*
135      * In principle, a Multimap implementation could add e3 first before failing on the null. But
136      * that seems unlikely enough to be worth complicating the test over, especially if there's any
137      * chance that a permissive test could mask a bug.
138      */
139     expectUnchanged();
140     // Be extra thorough in case internal state was corrupted by the expected null.
141     assertEquals(Lists.newArrayList(), Lists.newArrayList(multimap().get(sampleKeys().e3)));
142     assertEquals(size, multimap().size());
143   }
144 
145   @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS})
146   public void testPutAllOnPresentNullKey() {
147     assertTrue(multimap().putAll(null, Lists.newArrayList(sampleValues().e3, sampleValues().e4)));
148     assertGet(null, sampleValues().e3, sampleValues().e4);
149   }
150 
151   @MapFeature.Require(absent = ALLOWS_NULL_KEYS)
152   public void testPutAllNullForbidden() {
153     try {
154       multimap().putAll(null, Collections.singletonList(sampleValues().e3));
155       fail("Expected NullPointerException");
156     } catch (NullPointerException expected) {
157       // success
158     }
159   }
160 
161   private static final Object[] EMPTY = new Object[0];
162 
163   @MapFeature.Require(SUPPORTS_PUT)
164   public void testPutAllEmptyCollectionOnAbsentKey() {
165     assertFalse(multimap().putAll(sampleKeys().e3, Collections.<V>emptyList()));
166     expectUnchanged();
167   }
168 
169   @MapFeature.Require(SUPPORTS_PUT)
170   public void testPutAllEmptyIterableOnAbsentKey() {
171     Iterable<V> iterable = new Iterable<V>() {
172       @Override
173       public Iterator<V> iterator() {
174         return Iterators.emptyIterator();
175       }
176     };
177 
178     assertFalse(multimap().putAll(sampleKeys().e3, iterable));
179     expectUnchanged();
180   }
181 
182   @CollectionSize.Require(absent = ZERO)
183   @MapFeature.Require(SUPPORTS_PUT)
184   public void testPutAllEmptyIterableOnPresentKey() {
185     multimap().putAll(sampleKeys().e0, Collections.<V>emptyList());
186     expectUnchanged();
187   }
188   
189   @MapFeature.Require(SUPPORTS_PUT)
190   public void testPutAllOnlyCallsIteratorOnce() {
191     Iterable<V> iterable = new Iterable<V>() {
192       private boolean calledIteratorAlready = false;
193 
194       @Override
195       public Iterator<V> iterator() {
196         checkState(!calledIteratorAlready);
197         calledIteratorAlready = true;
198         return Iterators.forArray(sampleValues().e3);
199       }
200     };
201     
202     multimap().putAll(sampleKeys().e3, iterable);
203   }
204 
205   @MapFeature.Require(SUPPORTS_PUT)
206   public void testPutAllPropagatesToGet() {
207     Collection<V> getCollection = multimap().get(sampleKeys().e0);
208     int getCollectionSize = getCollection.size();
209     assertTrue(multimap().putAll(
210         sampleKeys().e0, Lists.newArrayList(sampleValues().e3, sampleValues().e4)));
211     assertEquals(getCollectionSize + 2, getCollection.size());
212     assertThat(getCollection).has().allOf(sampleValues().e3, sampleValues().e4);
213   }
214 }